package modelo;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

import org.joda.time.LocalDate;
import org.uqbar.commons.model.UserException;
import org.uqbar.commons.utils.TransactionalAndObservable;

import applicationModels.IntObservable;
import database.Tienda;

@TransactionalAndObservable
public class Empleado implements Serializable{

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private String nombre;
	private String apellido;
	private int legajo;
	private List<Disponibilidad> disponibilidades;
	private Set<Excepcion> excepciones;
	private TreeSet<Asignacion> asignaciones;

	public Empleado(String nombre, String apellido) {
		this.nombre = nombre;
		this.apellido = apellido;
		this.legajo = Tienda.getInstance().getSigLegajo();
		disponibilidades = new ArrayList<Disponibilidad>();
		inicializarDisponibilidades();
		excepciones = new TreeSet<Excepcion>();
		this.asignaciones = new TreeSet<Asignacion>();
	}
	
	/**
	 * Este constructor se usa solamente en las vistas para generar un empleado
	 * vacio y bindear sus datos. No usar en otro lado!
	 */
	public Empleado() {
		this.legajo = Tienda.getInstance().getSigLegajo();
		disponibilidades = new ArrayList<Disponibilidad>();
		inicializarDisponibilidades();
		excepciones = new TreeSet<Excepcion>();
		this.legajo = Tienda.getInstance().getSigLegajo();
		disponibilidades = new ArrayList<Disponibilidad>();
		inicializarDisponibilidades();
		excepciones = new TreeSet<Excepcion>();
		this.asignaciones = new TreeSet<Asignacion>();
	}
	
	/**
	 * Un empleado nuevo puede trabajar todos los dias en todos los horarios. 
	 * Derecho de piso?
	 */
	private void inicializarDisponibilidades() {
		for(Dia dia:Dia.values()){
			disponibilidades.add(new Disponibilidad(dia, new Horario(new IntObservable(0), new IntObservable(23))));
		}
	}
	
	public String getApellido() {
		return apellido;
	}
	
	public void setApellido(String apellido) {
		if(apellido == null || apellido.isEmpty() ){
			throw new UserException("El apellido es un campo requerido");
		}
		this.apellido = apellido;
		
	}

	public String getNombre() {
		return nombre;
	}
	
	public void setNombre(String nombre) {
		if(nombre == null || nombre.isEmpty() ){
			throw new UserException("El nombre es un campo requerido");
		}
		this.nombre = nombre;
	}

	public int getLegajo() {
		return legajo;
	}
	
	public String getIdentificacionCompleta(){
		return "Leg "+legajo+" - "+apellido+", "+nombre;
	}
	
	public List<Excepcion> getExcepciones(){
		return new ArrayList<Excepcion>(excepciones);
	}

	public List<Disponibilidad> getDisponibilidades() {
		return disponibilidades;
	}

	/**
	 * Metodo *exclusivo* para facilitar los tests!
	 * 
	 * @param nuevaDisponibilidad
	 */
	public void setDisponibilidades(List<Disponibilidad> disponibilidades) {
		this.disponibilidades = disponibilidades;
	}

	
	
	public TreeSet<Asignacion> getAsignaciones() {
		return asignaciones;
	}

	public void setAsignaciones(TreeSet<Asignacion> asignaciones) {
		this.asignaciones = asignaciones;
	}

	public void setLegajo(int legajo) {
		this.legajo = legajo;
	}

	public void setExcepciones(Set<Excepcion> excepciones) {
		this.excepciones = excepciones;
	}

	public void modificarDisponibilidad(Dia dia, Horario horario) {
		for(Disponibilidad disp : this.disponibilidades){
			if(disp.getDia().equals(dia)){
				disp.modificarDisponibilidad(horario);
			}
		}
	}
	
	public boolean puedeTrabajar(Asignacion asignacion){
		if(this.asignaciones.contains(asignacion)){
			return false;
		}else{
			return this.podriaTrabajar(asignacion.getFecha(), asignacion.getHorario());
		}
	}
	
	

	private boolean podriaTrabajar(LocalDate fecha, Horario horario) {
		if(this.tieneExcepcion(fecha)){
			return this.getExcepcion(fecha).puedeTrabajar();
		}else{
			return this.estaDisponible(Dia.getDia(fecha), horario);
		}
	}

	public boolean estaDisponible(Dia dia, Horario horario) {
		for(Disponibilidad disp : this.disponibilidades){
			if(disp.getDia().equals(dia)){
				return disp.existeDisponibilidad(horario);
			}
		}
		return false; //Puesto para que Eclipse no se queje. >.< 
		//Nunca llegara aca, los empleados se crean por defecto con todos los dias y no existe forma de agregarles o sacarles ninguno.
	}
	
	public void agregarRestriccion(LocalDate fecha, String descripcion){
		if(!excepciones.add(new Restriccion(fecha, descripcion))){
			throw new UserException("El empleado ya tiene una excepcion para esa fecha.");
		}
	}
	
	public void agregarLibertad(LocalDate fecha, String descripcion){
		if(!excepciones.add(new Libertad(fecha, descripcion))){
			throw new UserException("El empleado ya tiene una excepcion para esa fecha.");
		}
	}
	
	public boolean tieneExcepcion(LocalDate fecha){
		for(Excepcion excepcion:excepciones){
			if(excepcion.getFecha().equals(fecha)){
				return true;
			}
		}	
		return false;
	}
	
	public void quitarExcepcion(Excepcion excepcion){
		excepciones.remove(excepcion);
	}
		
	public Excepcion getExcepcion(LocalDate fecha){
		for(Excepcion excepcion:excepciones){
			if(excepcion.getFecha().equals(fecha)){
				return excepcion;
			}
		}
		return null;
	}

	

	public void agregarAsignacion(Asignacion asignacion) {
		this.asignaciones.add(asignacion);
	}

	public void quitarAsignacion(Asignacion asignacion) {
		this.asignaciones.remove(asignacion);
	}

	/**
	 * Este metodo se usa para clonar un empleado en otro objeto.
	 * Se necesita para volver atras los cambios cuando el usuario edita campos del empleado pero 
	 * clickea el boton de cancelar.
	 * @return una copia del empleado
	 */
	public Empleado clonar() {
		Empleado copia = new Empleado();
		copia.setNombre(this.nombre);
		copia.setApellido(this.apellido);
		copia.setLegajo(this.legajo);
		copia.setAsignaciones(clonarAsignaciones());
		copia.setDisponibilidades(clonarDisponibilidades());
		copia.setExcepciones(this.excepciones);
		return copia;
	}

	private List<Disponibilidad> clonarDisponibilidades() {
		List<Disponibilidad> copia = new ArrayList<Disponibilidad>();
		for(Disponibilidad disp : this.getDisponibilidades()){
			copia.add(disp.clonar());
		}
		return copia;
	}

	private TreeSet<Asignacion> clonarAsignaciones() {
		TreeSet<Asignacion> copia = new TreeSet<Asignacion>();
		for(Asignacion a : this.asignaciones){
			copia.add(a.clonar());
		}
		return copia;
	}

	/**
	 * Recupera los datos guardados en el objeto backup
	 * @param backup
	 */
	public void backup(Empleado backup) {
		this.setNombre(backup.nombre);
		this.setApellido(backup.apellido);
		this.setLegajo(backup.legajo);
		this.setAsignaciones(backup.asignaciones);
		this.setDisponibilidades(backup.disponibilidades);
		this.setExcepciones(backup.excepciones);
	}
	
	

	

	

}
